package manager

import (
	"fmt"
	"log"
	"sort"
	"strings"
	"sync"

	"github.com/Kindling-project/chaos-backend/api"
	"github.com/Kindling-project/chaos-backend/tools/kubernetescli"
	cli "github.com/Kindling-project/chaos-backend/tools/kubernetescli"
	"github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
	"github.com/spf13/viper"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

const (
	NoOption     string = "no-option"
	NoAction     string = "no-action"
	Unstructured string = "unstructured"
)

var DefaultTemplateMap *ChaosTemplateMap

func SetupChaosTemplate(configPath string) {
	DefaultTemplateMap = ReadTemplate(configPath)
}

type ChaosTemplateMap struct {
	ChaosActionMap    map[string]ChaosTemplate `mapstructure:"chaos_action_map"`
	ChaosTemplateList []ChaosTemplate          `mapstructure:"chaos_template_list"`

	chaosListCache sync.Map
}

type ChaosTemplate struct {
	// 故障在故障模板中的序号
	ChaosNumber int `mapstructure:"chaos_number"`

	// 易于解释的故障唯一索引,可以置空,置空后会根据ChaosType字段自动生成
	ComplexAction string `mapstructure:"complex_action"`
	// 故障类型,在ComplexAction未指定时作为辅助信息
	ChaosType string `mapstructure:"chaos_type"`

	// 开发者对该故障的描述
	Describe string `mapstructure:"describe"`
	// 哪些服务可以支持该故障
	SupportServices []string `mapstructure:"support_services"`
	// 是否需要检查该故障的入口
	CheckEntryDefined bool `mapstructure:"check_entry_defined"`
	// 是否禁用自动创建的PodSelector和mode:all
	DisablePodSelector bool `mapstructure:"disable_pod_selector"`
	// 该故障需要提供给用户的提示
	Note map[string]string `mapstructure:"note"`

	// 故障定义模板,只在非结构化故障定义时使用
	SpecTemplate map[string]any `mapstructure:"spec_template"`

	// TODO 暂未使用,支持对通过代码内置的故障设置场景参数
	Params map[string]any `mapstructure:"params"`
}

func (t *ChaosTemplate) CheckNote(instance string) string {
	if t.Note == nil {
		return ""
	}

	if note, ok := t.Note[instance]; ok {
		return note
	} else if note, ok := t.Note["common"]; ok {
		return note
	}

	return ""
}

func ReadTemplate(configPath string) *ChaosTemplateMap {
	var vip = viper.New()
	vip.SetConfigFile(configPath)
	if err := vip.ReadInConfig(); err != nil {
		return nil
	}

	templateMap := ChaosTemplateMap{}
	if err := vip.Unmarshal(&templateMap); err != nil {
		// TODO Error Info
		return nil
	}

	templateMap.ChaosActionMap = make(map[string]ChaosTemplate)
	for index := 0; index < len(templateMap.ChaosTemplateList); index++ {
		item := &templateMap.ChaosTemplateList[index]
		if item.ComplexAction == "" {
			if item.ChaosType == "" {
				log.Fatalf("must set chaos_type for template item while complex_action is empty:\n%+v", item)
			}
			item.ComplexAction = fmt.Sprintf("%s#%s#%d", item.ChaosType, Unstructured, index)
			// 添加非结构化故障实例到Manager
			chaosType := caseDesensitization(item.ChaosType)
			if chaosType == nil {
				log.Fatalf("failed when reading templateMap: chaosType %s is illegal", item.ChaosType)
			}
			DefaultManager.RegisterUnstructuredChaos(&cli.UnstructuredChaos{
				ChaosKind: *chaosType,
				GVR: schema.GroupVersionResource{
					Group:    v1alpha1.GroupVersion.Group,
					Version:  v1alpha1.GroupVersion.Version,
					Resource: strings.ToLower(string(*chaosType)),
				},
				ChaosSpecTemplate:   item.SpecTemplate,
				Namespace:           "train-ticket",
				WithAutoPodSelector: !item.DisablePodSelector,
			}, item.ComplexAction)
		}
		templateMap.ChaosActionMap[item.ComplexAction] = *item
		item.ChaosNumber = index
	}
	return &templateMap
}

func (ctm *ChaosTemplateMap) AddChaosItemFromTemplate(chaos_list []api.Chaos, chaosTemplate ChaosTemplate, instance string) []api.Chaos {
	if chaosTemplate.CheckEntryDefined {
		if _, ok := kubernetescli.DefaultServiceMethodEntryMap.CheckService(instance); !ok {
			return chaos_list
		}
	}

	if len(chaosTemplate.SupportServices) > 0 {
		var supported bool = false
		for _, service := range chaosTemplate.SupportServices {
			if service == instance {
				supported = true
				break
			}
		}

		if !supported {
			return chaos_list
		}
	}

	// 对于front来说,只需要关心最终的complexAction和Params,目前只有一个instance,即用户选择的故障
	chaos_list = append(chaos_list, api.Chaos{
		Desc:          chaosTemplate.Describe,
		ComplexAction: chaosTemplate.ComplexAction,
		Params: []api.ChaosParams{
			{Name: "instance", Type: "string"},
		},
		Note:        chaosTemplate.CheckNote(instance),
		ChaosNumber: chaosTemplate.ChaosNumber,
	})

	return chaos_list
}

func (ctm *ChaosTemplateMap) SearchForUnsatisfiedNote(instance string, action string) (note string, ok bool) {
	if template, ok := ctm.ChaosActionMap[action]; ok && template.Note != nil {
		note, ok = template.Note[instance]
		return note, ok
	}

	return "", false
}

func (ctm *ChaosTemplateMap) AddNoteAfterInjected(chaos *api.Chaos) {
	var instance string

	if chaos == nil || chaos.Params == nil {
		return
	}

	for _, param := range chaos.Params {
		if param.Name == "instance" {
			instance = param.Value
			break
		}
	}

	if instance == "" {
		return
	}

	note, ok := ctm.SearchForUnsatisfiedNote(instance, chaos.ComplexAction)

	if ok {
		chaos.Note = note
	}
}

// ListChaosTemplate 根据用户指定的实例名, 从模板中筛选出支持的故障场景
func (ctm *ChaosTemplateMap) ListChaosTemplate(instance string) []api.Chaos {
	if chaosList, ok := ctm.chaosListCache.Load(instance); ok {
		return chaosList.([]api.Chaos)
	}

	chaos_list := make([]api.Chaos, 0)

	for _, item := range ctm.ChaosTemplateList {
		chaos_list = ctm.AddChaosItemFromTemplate(chaos_list, item, instance)
	}

	sort.Slice(chaos_list, func(i, j int) bool {
		return chaos_list[i].ChaosNumber < chaos_list[j].ChaosNumber
	})

	ctm.chaosListCache.Store(instance, chaos_list)
	return chaos_list
}

func AddNoteAfterInjected(chaos *api.Chaos) {
	DefaultTemplateMap.AddNoteAfterInjected(chaos)
}

func ListChaosTemplate(instance string) []api.Chaos {
	return DefaultTemplateMap.ListChaosTemplate(instance)
}

var allChaosTemplateType = []v1alpha1.TemplateType{
	v1alpha1.TypeSchedule,
	v1alpha1.TypeAWSChaos,
	v1alpha1.TypeAzureChaos,
	v1alpha1.TypeBlockChaos,
	v1alpha1.TypeDNSChaos,
	v1alpha1.TypeGCPChaos,
	v1alpha1.TypeHTTPChaos,
	v1alpha1.TypeIOChaos,
	v1alpha1.TypeJVMChaos,
	v1alpha1.TypeKernelChaos,
	v1alpha1.TypeNetworkChaos,
	v1alpha1.TypePhysicalMachineChaos,
	v1alpha1.TypePodChaos,
	v1alpha1.TypeStressChaos,
	v1alpha1.TypeTimeChaos,
}

// caseDesensitization 允许开发者在编写模板时,对chaosType大小写不敏感
func caseDesensitization(chaosType string) *v1alpha1.TemplateType {
	for _, item := range allChaosTemplateType {
		if strings.EqualFold(string(item), chaosType) {
			return &item
		}
	}
	return nil
}
